import {
  random_id,
  average2,
  get_by_id,
  get_id,
  note_coords,
  mod,
} from "./utils";
export class Coordinates {
  constructor(layer_elem, mei) {
    this.pnames = "cdefgab";
    this.layer_elem = layer_elem;
    this.mei = mei;
    this.score_elem = mei
      .getElementsByTagName("body")[0]
      .getElementsByTagName("score");

    this.measure_map = this.compute_measure_map();
    this.finalize_draw_context(this.layer_elem);
  }

  // 缩放SVG切换为doc
  // https://stackoverflow.com/questions/22183727/how-do-you-convert-screen-coordinates-to-document-space-in-a-scaled-svg
  getPointerSVGCoords(svg, pointer = { x: 0, y: 0 }) {
    var system = svg.getElementsByClassName("system")[0];

    console.log(system);
    console.log(system.parentElement);
    var pt = svg.createSVGPoint();
    pt.x = pointer.x;
    pt.y = pointer.y;
    return pt.matrixTransform(system.parentElement.getScreenCTM().inverse());
  }

  place_note(e) {
    var pointer = { x: e.clientX, y: e.clientY };

    var elem = document.elementFromPoint(pointer.x, pointer.y);
    console.log(elem);

    let [pname, oct, note] = this.note_params(pointer);
    if (!pname) return;
    var new_element_id = "new-" + random_id();
    var added = [];
    // 暂时画出来
    added.push(this.draw_note(pname, oct, note, true, new_element_id));
    // Add it to the current layer
    // added.push(this.add_note(pname, oct, note, true, new_element_id));

    console.log(added);
  }
  note_params(pointer) {
    const svg = this.layer_elem.children[0];
    const pt = this.getPointerSVGCoords(svg, pointer);
    console.log(pt.x, pt.y);
    const [measure, measureIndex] = this.coord_measure(pt, this.measure_map);
    const staff = this.coord_staff(pt, measure);
    const [pname, oct] = this.coord_pitch(pt, staff);
    const sim_note = this.closest_note(pt, staff);
    const sim_chord = this.closest_note_chord(pt, staff);
    if (!sim_note) return [null, null, null];
    //  const [rel_event,simul] = coord_event(pt, staff, measure);
    console.log(staff);
    console.log(sim_note);

    sim_note.setAttribute("fill", "red");

    document.getElementById("measure").innerHTML = "小节:" + measureIndex;
    document.getElementById("note").innerHTML = "音符:" + sim_note;

    return [pname, oct, sim_note];
  }

  draw_note(pname, oct, note, sim = true, id = "") {
    var curr_elem = document.getElementById(id);
    var added = [];
    if (curr_elem) curr_elem.parentElement.removeChild(curr_elem);
    if (sim) {
      var new_note = note_coords(note)[0];
      console.log(new_note);
      let [x, y] = this.note_params_coords_sim(pname, oct, note);
      console.log(note);
      console.log(x, y);

      // 复制音符
      var g = document.createElementNS("http://www.w3.org/2000/svg", "g");
      var gh = document.createElementNS("http://www.w3.org/2000/svg", "g");
      var u = document.createElementNS("http://www.w3.org/2000/svg", "use");
      // Same notehead
      u.setAttributeNS(
        "http://www.w3.org/1999/xlink",
        "href",
        note
          .getElementsByTagName("use")[0]
          .getAttributeNS("http://www.w3.org/1999/xlink", "href")
      );
      // And scale and place it appropriately
      u.setAttribute("x", x - 100);
      u.setAttribute("y", y - 180);
      u.setAttribute("height", "720px");
      u.setAttribute("width", "720px");
      u.setAttribute("fill", "#EE6363");
      g.id = id;
      g.classList.add("note");
      gh.classList.add("notehead");
      gh.appendChild(u);
      g.appendChild(gh);
      note.parentElement.appendChild(g);
      g.onclick = function(ev) {
        this.toggle_selected(g, ev);
      };
      added.push(g);
    }
    return added.reverse();
  }
  add_note(pname, oct, note, sim = true, id = "") {
    console.log(get_id(this.mei, note));
    var l = get_by_id(this.mei, get_id(this.mei, note));

    if (!this.score_elem[0].contains(l)) {
      console.log("音符不在乐谱中?");
      return false;
    }
    var n = this.mei.createElement("note");
    var added = [];
    n.setAttribute("xml:id", id);
    if (sim) {
    }
  }

  // 从预先计算的小节列表右边缘，查找当前小节
  coord_measure(pt, measure_map) {
    let idx = -1;
    let r = measure_map.find((p, index) => {
      idx = index;
      return p[0] > pt.x;
    });
    if (r) {
      return [r[1], idx];
    } else {
      return [undefined, idx];
    }
  }
  // 预先计算的小节列表右边缘
  compute_measure_map() {
    let svg = this.layer_elem.children[0];
    var measures = Array.from(svg.getElementsByClassName("measure"));

    // 让每个小节的右边缘组成网格线
    var measure_map = measures.map((msr) => [
      msr.getBBox().x + msr.getBBox().width,
      msr,
    ]);
    measure_map.sort((x, y) => x[0] - y[0]);
    return measure_map;
    // 也许可以把s.id换成MEI里工作人员的“n”
  }

  coord_staff(pt, measure) {
    var staves = Array.from(measure.getElementsByClassName("staff"));
    var stave_coords = staves.map((s) => [this.staff_midpoint(s), s]);

    stave_coords.sort((a, b) => a[0] - b[0]);
    var index_maybe = stave_coords.findIndex((s) => pt.y < s[0]);
    console.log(index_maybe);

    // 有可能有声乐部分
    if (index_maybe == 0) {
      return stave_coords[0][1];
    }
    if (index_maybe == -1) {
      return stave_coords[stave_coords.length - 1][1];
    }

    const divider = average2(
      stave_coords[index_maybe - 1][0],
      stave_coords[index_maybe][0]
    );
    if (pt.y < divider) return stave_coords[index_maybe - 1][1];
    else {
      return stave_coords[index_maybe][1];
    }
  }

  // 不允许和弦, 应用于 coord_staff.
  closest_note(pt, staff) {
    var notes = Array.from(staff.getElementsByClassName("note")).map((n) => [
      note_coords(n)[0],
      n,
    ]);
    console.log(notes);
    if (notes.length == 0) return null;
    notes.sort((a, b) => a[0] - b[0]);
    var index_maybe = notes.findIndex((n) => pt.x < n[0]);
    if (index_maybe == 0) return notes[0][1];
    if (index_maybe == -1) return notes[notes.length - 1][1];
    const divider = average2(notes[index_maybe - 1][0], notes[index_maybe][0]);
    if (pt.x < divider) return notes[index_maybe - 1][1];
    else return notes[index_maybe][1];
  }

  // 专用于和弦
  closest_note_chord() {}

  // eslint-disable-next-line no-unused-vars
  coord_event(pt, staff, measure) {}

  coord_pitch(pt, staff) {
    // eslint-disable-next-line no-unused-vars
    var n = staff.getElementsByClassName("note")[0];
    //  var [y_to_p,p_to_y] = pitch_grid(staff,n);
    return staff.y_to_p(pt.y);
  }

  patch_grid() {}

  // 为 svg 添加额外方法
  finalize_draw_context(layer_elem) {
    let svg = layer_elem.children[0];
    for (let n of svg.getElementsByClassName("note")) {
      n.onclick = function(ev) {
        this.toggle_selected(n, ev);
      };
    }
    for (let s of svg.getElementsByClassName("staff")) {
      //TODO: handle staves with no notes in them
      let [y_to_p, p_to_y] = this.pitch_grid(s);
      s.y_to_p = y_to_p;
      s.p_to_y = p_to_y;
    }
    // this.draw_graph(svg);
  }

  pitch_grid(staff) {
    const mid = this.staff_midpoint(staff);
    const thrd = -Math.abs(this.staff_third_distance(staff));
    const snd = thrd / 2;
    const ns = staff.getElementsByClassName("note");
    var note;
    if (ns.length > 0) {
      note = ns[0];
    } else {
      // 确保音符在当前小节
      const sys = staff.closest(".system");
      // 其他小节
      const staves = Array.from(sys.getElementsByClassName("staff"));
      // 用于等高对比
      const sibling_staff = staves.find(
        (st) =>
          this.staff_midpoint(st) == mid &&
          st.getElementsByClassName("note").length > 0
      );
      // 另一个用于对比高度的音符
      note = sibling_staff.getElementsByClassName("note")[0];
    }

    const mid_n =
      this.diatonic_note_n(note) -
      Math.floor((note_coords(note)[1] - mid) / snd);

    return [
      (y) => {
        // TODO: this may need adjustment by snd/2
        var diatonic_n = Math.floor((y + snd / 2 - mid) / snd) + mid_n;
        var oct = Math.floor(diatonic_n / 7);
        var note = this.pnames[mod(diatonic_n, 7)];

        return [note, oct];
      },
      (pname, oct) => {
        var diatonic_n = oct * 7 + this.pnames.indexOf(pname);
        return mid + (diatonic_n - mid_n) * snd;
      },
    ];
  }

  // Verovio 将线谱staff分为独立的5个部分，根据中间位置进行计算
  staff_midpoint(staff) {
    let mid_rect = staff.children[2] // 中间线
      .getBBox();
    return mid_rect.y + mid_rect.height / 2;
  }
  staff_third_distance(staff) {
    // 两条弦线之间的线是两个音符之间相隔三分之一的距离

    let mid_rect = staff.children[2].getBBox(); // 中间线(第三线)

    let end_rect = staff.children[1].getBBox(); // 结束线

    return Math.abs(mid_rect.y - end_rect.y);
  }

  // 绘制已存在标记
  // eslint-disable-next-line no-unused-vars
  draw_graph(draw_context, mei_graph) {}

  // 全音音符
  diatonic_note_n(note) {
    // 返回一个音符，从C0开始
    var mei_note = get_by_id(this.mei, get_id(this.mei, note));
    var pname = mei_note.getAttribute("pname");
    var octave = mei_note.getAttribute("oct");
    // 假设上述方法目前有效
    return octave * 7 + this.pnames.indexOf(pname);
  }

  note_params_coords_sim(pname, oct, note) {
    var staff = note.closest(".staff");
    // eslint-disable-next-line no-unused-vars
    var n = staff.getElementsByClassName("note")[0];
    return [note_coords(note)[0], staff.p_to_y(pname, oct)];
  }

  // eslint-disable-next-line no-unused-vars
  toggle_selected(item, ev) {
    console.log(`toggle_selected ${item}`);
  }
}
